﻿using Microsoft.Extensions.Configuration;
using RuoVea.ExConfig.Options;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace RuoVea.ExConfig;

/// <summary>
/// 配置文件
/// </summary>
public class AppSettings
{
    /// <summary>
    /// 
    /// </summary>
    public static IConfiguration Configuration { get; set; }

    /// <summary>
    /// 
    /// </summary>
    static AppSettings()
    {
        var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
        // 获取程序执行目录
        var executeDirectory = AppContext.BaseDirectory;

        ConfigSettingsOptions configSettings = new ConfigSettingsOptions()
        {
            // 读取忽略的配置文件
            IgnoreConfigurationFiles = new List<string> {
                "IgnoreConfigurationFiles",
                "IgnoreConfiguration",
                "IgnoreConfig",
                "IgnoreDirectories"
            },
            // 获取自定义配置扫描目录
            ScanDirectories = new List<string>() {
                "ConfigurationFiles",
                "Configuration",
                "Config",
                "ScanDirectories"
            },
        };

        var configurationBuilder = new ConfigurationBuilder().SetBasePath(Environment.CurrentDirectory);

        List<string> ignoreConfigurationFiles = new List<string>();
        List<string> scanDirectories = new List<string>();

        configSettings.IgnoreConfigurationFiles.ForEach(x => { if (Directory.Exists(Path.Combine(executeDirectory, x))) ignoreConfigurationFiles.Add(x); });
        configSettings.ScanDirectories.ForEach(x => { if (Directory.Exists(Path.Combine(executeDirectory, x))) scanDirectories.Add(x); });


        // 扫描执行目录及自定义配置目录下的 *.json 文件
        var jsonFiles = new[] { executeDirectory }
        .Concat(scanDirectories)
        .SelectMany(u => Directory.GetFiles(u, "*.json", SearchOption.TopDirectoryOnly));


        /*排序的配置文件前缀*/
        string[] excludeJsonPrefixs = new[] { "appsettings", "appsettings.*", "bundleconfig", "compilerconfig" };
        // 处理控制台应用程序
        var _excludeJsonPrefixs = excludeJsonPrefixs.Where(u => !u.Equals("appsettings"));

        /*排除运行时 Json 后缀*/
        string[] runtimeJsonSuffixs = new[] { "deps.json", "runtimeconfig.dev.json", "runtimeconfig.prod.json", "runtimeconfig.json" };

        // 将所有文件进行分组
        IEnumerable<IGrouping<string, string>> jsonFilesGroups = jsonFiles.GroupBy((string file) =>
            {
                // 根据 . 分隔
                var fileNameParts = Path.GetFileName(file).Split('.', StringSplitOptions.RemoveEmptyEntries);
                if (fileNameParts.Length == 2) return fileNameParts[0];

                return string.Join('.', fileNameParts.Take(fileNameParts.Length - 2));
            })
                .Where(u => !_excludeJsonPrefixs.Contains(u.Key, StringComparer.OrdinalIgnoreCase) &&
                !u.Any(c => runtimeJsonSuffixs.Any(z => c.EndsWith(z, StringComparison.OrdinalIgnoreCase))
                || ignoreConfigurationFiles.Contains(Path.GetFileName(c), StringComparer.OrdinalIgnoreCase)));

        // 遍历所有配置分组
        foreach (var group in jsonFilesGroups)
        {
            // 限制查找的 json 文件组
            var limitFileNames = new[] { $"{group.Key}.json", $"{group.Key}.{envName}.json" };
            // 查找默认配置和环境配置
            var files = group.Where(u => limitFileNames.Contains(Path.GetFileName(u), StringComparer.OrdinalIgnoreCase)).OrderBy(u => Path.GetFileName(u).Length);
            // 循环加载                                   
            foreach (var jsonFile in files)
            {
                configurationBuilder.AddJsonFile(jsonFile, optional: true, reloadOnChange: true);
            }
        }
        //configurationBuilder.AddAgileConfig(configClient, arg => Console.WriteLine($"config changed , action:{arg.Action} key:{arg.Key}"));
        Configuration = configurationBuilder.Build();
    }

    /// <summary>
    /// 
    /// </summary>
    public AppSettings(Action<IConfigurationBuilder> builder)
    {
        var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
        // 获取程序执行目录
        var executeDirectory = AppContext.BaseDirectory;

        ConfigSettingsOptions configSettings = new ConfigSettingsOptions()
        {
            // 读取忽略的配置文件
            IgnoreConfigurationFiles = new List<string> {
                "IgnoreConfigurationFiles",
                "IgnoreConfiguration",
                "IgnoreConfig",
                "IgnoreDirectories"
            },
            // 获取自定义配置扫描目录
            ScanDirectories = new List<string>() {
                "ConfigurationFiles",
                "Configuration",
                "Config",
                "ScanDirectories"
            },
        };

        IConfigurationBuilder configurationBuilder = new ConfigurationBuilder().SetBasePath(Environment.CurrentDirectory);
        
        if(configurationBuilder!=null)
            builder.Invoke(configurationBuilder);

        List<string> ignoreConfigurationFiles = new List<string>();
        List<string> scanDirectories = new List<string>();

        configSettings.IgnoreConfigurationFiles.ForEach(x => { if (Directory.Exists(Path.Combine(executeDirectory, x))) ignoreConfigurationFiles.Add(x); });
        configSettings.ScanDirectories.ForEach(x => { if (Directory.Exists(Path.Combine(executeDirectory, x))) scanDirectories.Add(x); });


        // 扫描执行目录及自定义配置目录下的 *.json 文件
        var jsonFiles = new[] { executeDirectory }
        .Concat(scanDirectories)
        .SelectMany(u => Directory.GetFiles(u, "*.json", SearchOption.TopDirectoryOnly));


        /*排序的配置文件前缀*/
        string[] excludeJsonPrefixs = new[] { "appsettings", "appsettings.*", "bundleconfig", "compilerconfig" };
        // 处理控制台应用程序
        var _excludeJsonPrefixs = excludeJsonPrefixs.Where(u => !u.Equals("appsettings"));

        /*排除运行时 Json 后缀*/
        string[] runtimeJsonSuffixs = new[] { "deps.json", "runtimeconfig.dev.json", "runtimeconfig.prod.json", "runtimeconfig.json" };

        // 将所有文件进行分组
        IEnumerable<IGrouping<string, string>> jsonFilesGroups = jsonFiles.GroupBy((string file) =>
        {
            // 根据 . 分隔
            var fileNameParts = Path.GetFileName(file).Split('.', StringSplitOptions.RemoveEmptyEntries);
            if (fileNameParts.Length == 2) return fileNameParts[0];

            return string.Join('.', fileNameParts.Take(fileNameParts.Length - 2));
        })
                .Where(u => !_excludeJsonPrefixs.Contains(u.Key, StringComparer.OrdinalIgnoreCase) &&
                !u.Any(c => runtimeJsonSuffixs.Any(z => c.EndsWith(z, StringComparison.OrdinalIgnoreCase))
                || ignoreConfigurationFiles.Contains(Path.GetFileName(c), StringComparer.OrdinalIgnoreCase)));

        // 遍历所有配置分组
        foreach (var group in jsonFilesGroups)
        {
            // 限制查找的 json 文件组
            var limitFileNames = new[] { $"{group.Key}.json", $"{group.Key}.{envName}.json" };
            // 查找默认配置和环境配置
            var files = group.Where(u => limitFileNames.Contains(Path.GetFileName(u), StringComparer.OrdinalIgnoreCase)).OrderBy(u => Path.GetFileName(u).Length);
            // 循环加载                                   
            foreach (var jsonFile in files)
            {
                configurationBuilder.AddJsonFile(jsonFile, optional: true, reloadOnChange: true);
            }
        }
        //configurationBuilder.AddAgileConfig(configClient, arg => Console.WriteLine($"config changed , action:{arg.Action} key:{arg.Key}"));
        Configuration = configurationBuilder.Build();
    }


    #region 封装要操作的字符 app
    /// <summary>
    /// 封装要操作的字符
    /// </summary>
    /// <param name="sections">节点配置</param>
    /// <returns></returns>
    public static string app(params string[] sections)
    {
        try
        {
            if (sections.Any())
            {
                return Configuration[string.Join(":", sections)];
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("appsettings.json 中不存在节点" + sections.ToString(), ex);
        }
        return "";
    }
    /// <summary>
    /// 递归获取配置信息数组
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sections"></param>
    /// <returns></returns>
    public static List<T> app<T>(params string[] sections)
    {
        List<T> list = new List<T>();
        // 引用 Microsoft.Extensions.Configuration.Binder 包
        Configuration.Bind(string.Join(":", sections), list);
        return list;
    }
    #endregion

    #region 获取配置 GetSection
    /// <summary>
    /// 获取配置
    /// </summary>
    /// <param name="key">key</param>
    /// <returns>IConfigurationSection</returns>
    public static IConfigurationSection GetSection(string key)
    {
        return Configuration.GetSection(key);
    }
    #endregion

    #region 封装要操作的字符 GetValue
    /// <summary>
    /// 封装要操作的字符
    /// </summary>
    /// <param name="sections"></param>
    /// <returns>object</returns>
    public static object GetValue(params string[] sections)
    {
        try
        {
            var val = string.Empty;
            for (int i = 0; i < sections.Length; i++)
            {
                val += sections[i] + ":";
            }
            return Configuration[val.TrimEnd(':')];
        }
        catch (Exception ex)
        {
            Console.WriteLine("appsettings.json 中不存在节点" + sections.ToString(), ex);
            return default(string);
        }
    }
    #endregion

    #region 获取配置 GetConfig
    /// <summary>
    /// 获取配置
    /// </summary>
    /// <typeparam name="T">强类型选项类</typeparam>
    /// <param name="path">配置中对应的Key</param>
    /// <param name="loadPostConfigure"></param>
    /// <returns>TOptions</returns>
    public static T GetConfig<T>(string path, bool loadPostConfigure = false)
    {
        var options = Configuration.GetSection(path).Get<T>();

        // 加载默认选项配置
        if (loadPostConfigure && typeof(IConfigurableOptions).IsAssignableFrom(typeof(T)))
        {
            var postConfigure = typeof(T).GetMethod("PostConfigure");
            if (postConfigure != null)
            {
                options ??= Activator.CreateInstance<T>();
                postConfigure.Invoke(options, new object[] { options, Configuration });
            }
        }

        return options;
    }
    #endregion

    #region 获取配置 GetAppSettings
    /// <summary>
    /// 获取配置
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <returns>T</returns>
    public static T GetAppSettings<T>(string key) where T : class, new()
    {
        return Configuration.GetSection(key)?.Get<T>();
    } 
    #endregion

    #region 数据库链接字符串 ConnectionStrings
    /// <summary>
    /// 数据库链接字符串
    /// </summary>
    /// <param name="keyName"></param>
    /// <returns>string</returns>
    public static string ConnectionStrings(String keyName)
    {
        return GetValue("ConnectionStrings", keyName)?.ToString();
    }
    #endregion

    #region 添加节点 append
    /// <summary>
    /// 添加节点
    /// </summary>
    /// <param name="jsonStr"></param>
    /// <returns>bool</returns>
    public static bool append(string jsonStr)
    {
        string filName = Path.Combine(AppContext.BaseDirectory, "appsettings.json");
        string bodyValue;

        using (StreamReader sr = new StreamReader(filName))
        {
            bodyValue = sr.ReadToEnd();
            int index = bodyValue.LastIndexOf('}');
            bodyValue = bodyValue.Substring(0, index - 1);
            bodyValue = bodyValue + ",\r\n" + jsonStr + "\r\n }";
        }
        using (var fs = new FileStream(filName, FileMode.Open))
        {
            byte[] info = new UTF8Encoding(true).GetBytes(bodyValue);
            fs.Write(info, 0, info.Length);
            fs.Close();
            fs.Dispose();
        }

        return true;
    }
    #endregion
}
